﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

namespace ZXthex.EditorUI
{
    public class EUIWindowGenerator
    {
        [MenuItem("CONTEXT/Canvas/Generate Window Script")]
        public static void GenerateWindowScript(MenuCommand command)
        {
            const string kClassSuffix = "Window";

            var canvas = command.context as Canvas;
            var scaler = canvas.GetComponent<CanvasScaler>();

            if (scaler != null && scaler.uiScaleMode != CanvasScaler.ScaleMode.ConstantPixelSize)
            {
                Debug.LogWarning("Only support ConstantPixelSize mode");
            }

            string cs, csbinding;
            GetFilePath(canvas.gameObject.name +
                (canvas.gameObject.name.EndsWith(kClassSuffix) ? "" : kClassSuffix),
                out cs, out csbinding);

            string csText = @"//auto generate
using UnityEngine;
using UnityEditor;
using ZXthex.EditorUI;

public partial class #NAME#Window : EUIWindow
{
    
    private void OnEnable()
    {
        InitComponent();

        //custom code
    }
}
";

            string csbindingText = @"//auto generate
using UnityEngine;
using UnityEditor;
using ZXthex.EditorUI;

public partial class #NAME#Window
{
    
    private void InitComponent()
    {
        #INIT#
    }

    #VARIABLE#
}

";
            string indent = "        ";
            string init = GenerateCode(canvas, indent);
            indent = "    ";
            string var = GenerateFieldDeclaration(indent, "");


            var name = Path.GetFileNameWithoutExtension(cs).Replace(" ", "_");
            csText = csText.Replace("#NAME#", name);
            csbindingText = csbindingText.Replace("#NAME#", name).Replace("#VARIABLE#", var).Replace("#INIT#", init);

            File.WriteAllText(cs, csText);
            File.WriteAllText(csbinding, csbindingText);
            AssetDatabase.Refresh();
        }

        static void GetFilePath(string name, out string cs, out string csbinding)
        {
            var dir = "Assets/Editor/EditorUI";
            if (!AssetDatabase.IsValidFolder(dir))
            {
                Directory.CreateDirectory(dir);
            }
            cs = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(dir, name + ".cs"));
            csbinding = cs.Substring(0, cs.Length - 2) + "binding.cs";
        }

        static Dictionary<RectTransform, string> rt2fieldDic=new Dictionary<RectTransform, string>(61);
        static Dictionary<string, RectTransform> field2rtDic=new Dictionary<string, RectTransform>(61);
        static Dictionary<string, string> field2typeDic = new Dictionary<string, string>(61);

        static string GenerateCode(Canvas canvas, string indent = "", string firstIndent = "")
        {
            rt2fieldDic.Clear();
            field2rtDic.Clear();
            field2typeDic.Clear();

            StringBuilder strB = new StringBuilder(1024);
            bool first = true;
            for (int i = 0; i < canvas.transform.childCount; i++)
            {
                var child = canvas.transform.GetChild(i) as RectTransform;
                EUIHelper.TravlesalTransform(child,
                    c => c.parent as RectTransform,
                    c => c.childCount,
                    (c, ind) => c.GetChild(ind) as RectTransform,
                    c => c.parent.GetComponent<Button>() || c.parent.GetComponent<InputField>(),
                    (c) =>
                    {
                        GenerateCode(strB, c, indent, first?firstIndent:indent);
                        first = false;
                    }
                    );

            }
            return strB.ToString();
        }

        static void GenerateCode(StringBuilder strB, RectTransform rt, string indent = "", string firstIndent = "")
        {
            int type = 0;
            const int BUTTON_TYPE = 1;
            const int INPUTFIELD_TYPE = 2;
            const int TEXT_TYPE = 3;

            strB.Append(firstIndent);

            if (rt.GetComponent<Button>())
            {
                type = BUTTON_TYPE;
                strB.Append("//Create Button ");
            }
            else if(rt.GetComponent<InputField>())
            {
                type = INPUTFIELD_TYPE;
                strB.Append("//Create InputField ");
            }
            else if(rt.GetComponent<Text>())
            {
                type = TEXT_TYPE;
                strB.Append("//Create Text ");
            }
            else
            {
                strB.Append("//Create Panel ");
            }

            var variableName = rt.name;
            variableName = variableName.Replace(" ", "_").Replace("(", "").Replace(")","");
            while(field2rtDic.ContainsKey(variableName))
            {
                variableName += "x";
            }
            field2rtDic.Add(variableName, rt);
            rt2fieldDic.Add(rt, variableName);

            strB.Append(GetFullPath(rt));
            strB.Append(Environment.NewLine);

            WriteLine(strB, indent, "{");

            var oldIndent = indent;
            indent += "    ";

            if(type == BUTTON_TYPE)
            {
                field2typeDic.Add(variableName, "EditorButton");

                string content = "";
                var textComp = rt.GetComponentInChildren<Text>();
                if (textComp)
                {
                    content = textComp.text;
                }
                
                WriteLine(strB, indent, variableName, " = new EditorButton(\"", content, "\");");
            }
            else if(type == INPUTFIELD_TYPE)
            {
                field2typeDic.Add(variableName, "EditorInputField");

                WriteLine(strB, indent, variableName, " = new EditorInputField();");
            }
            else if(type == TEXT_TYPE)
            {
                field2typeDic.Add(variableName, "EditorText");
                string content = rt.GetComponent<Text>().text;
                
                WriteLine(strB, indent, variableName, " = new EditorText(\"",content,  "\");");
            }
            else
            {
                field2typeDic.Add(variableName, "EditorPanel");

                WriteLine(strB, indent, variableName, " = new EditorPanel();");
            }

            GenerateRTCode(strB, rt, indent);

            indent = oldIndent;

            WriteLine(strB, indent, "}");
            strB.Append(Environment.NewLine);


        }

        static void GenerateRTCode(StringBuilder strB, RectTransform rt, string indent)
        {
            var variable = rt2fieldDic[rt];
            if(rt.parent.GetComponent<Canvas>())
            {
                WriteLine(strB, indent, "canvas.AddChild(", variable, ");");
            }
            else
            {
                WriteLine(strB, indent, variable, ".SetParent(", rt2fieldDic[rt.parent as RectTransform], ");");
            }

            WriteLine(strB, indent, variable, ".anchoredPosition = new Vector2( ",
                rt.anchoredPosition.x.ToString(), "f, ", rt.anchoredPosition.y.ToString(), "f);");

            WriteLine(strB, indent, variable, ".sizeDelta = new Vector2( ",
                rt.sizeDelta.x.ToString(), "f, ", rt.sizeDelta.y.ToString(), "f);");

            WriteLine(strB, indent, variable, ".anchorMin = new Vector2( ",
                rt.anchorMin.x.ToString(), "f, ", rt.anchorMin.y.ToString(), "f);");

            WriteLine(strB, indent, variable, ".anchorMax = new Vector2( ",
                rt.anchorMax.x.ToString(), "f, ", rt.anchorMax.y.ToString(), "f);");

            WriteLine(strB, indent, variable, ".pivot = new Vector2( ",
                rt.pivot.x.ToString(), "f, ", rt.pivot.y.ToString(), "f);");
        }

        static void WriteLine(StringBuilder strB, string indent, string line)
        {
            strB.Append(indent);
            strB.Append(line);
            strB.Append(Environment.NewLine);
        }

        static void WriteLine(StringBuilder strB, string indent, string part0, string part1)
        {
            strB.Append(indent);
            strB.Append(part0);
            strB.Append(part1);
            strB.Append(Environment.NewLine);
        }

        static void WriteLine(StringBuilder strB, string indent, string part0, string part1, string part2)
        {
            strB.Append(indent);
            strB.Append(part0);
            strB.Append(part1);
            strB.Append(part2);
            strB.Append(Environment.NewLine);
        }

        static void WriteLine(StringBuilder strB, string indent, string part0, string part1, string part2, string part3)
        {
            strB.Append(indent);
            strB.Append(part0);
            strB.Append(part1);
            strB.Append(part2);
            strB.Append(part3);
            strB.Append(Environment.NewLine);
        }

        static void WriteLine(StringBuilder strB, string indent, 
            string part0, string part1, string part2, string part3, string part4)
        {
            strB.Append(indent);
            strB.Append(part0);
            strB.Append(part1);
            strB.Append(part2);
            strB.Append(part3);
            strB.Append(part4);
            strB.Append(Environment.NewLine);
        }

        static void WriteLine(StringBuilder strB, string indent, 
            string part0, string part1, string part2, string part3, string part4, string part5)
        {
            strB.Append(indent);
            strB.Append(part0);
            strB.Append(part1);
            strB.Append(part2);
            strB.Append(part3);
            strB.Append(part4);
            strB.Append(part5);
            strB.Append(Environment.NewLine);
        }

        static StringBuilder pathB = new StringBuilder(256);
        static Stack<string> pathStack = new Stack<string>(16);
        static string GetFullPath(RectTransform rt)
        {
            pathB.Clear();
            pathStack.Clear();

            do
            {
                pathStack.Push(rt.name);
                rt = rt.parent as RectTransform;
            }
            while (rt != null && !rt.GetComponent<Canvas>());
            
            while(pathStack.Count>0)
            {
                pathB.Append(pathStack.Pop());
                if (pathStack.Count > 0)
                    pathB.Append("/");
            }

            return pathB.ToString();
        }

        static StringBuilder fieldStrB = new StringBuilder(512);
        static string GenerateFieldDeclaration(string indent, string firstIndent)
        {
            fieldStrB.Clear();

            bool first = true;
            foreach(var v in field2rtDic.Keys)
            {
                if(first)
                {
                    fieldStrB.Append(firstIndent);
                    first = false;
                }
                else
                {
                    fieldStrB.Append(indent);
                }
                fieldStrB.Append(field2typeDic[v]);
                fieldStrB.Append(" ");
                fieldStrB.Append(v);
                fieldStrB.Append(";");
                fieldStrB.Append(Environment.NewLine);

            }
            return fieldStrB.ToString();
        }
    }
}